001 /* 002 * Copyright 2005 Stephen J. McConnell. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.transit; 020 021 import java.io.File; 022 import java.net.URL; 023 import java.net.URI; 024 import java.rmi.RemoteException; 025 import java.rmi.NoSuchObjectException; 026 import java.rmi.server.UnicastRemoteObject; 027 import java.util.EventObject; 028 import java.util.EventListener; 029 030 import net.dpml.transit.info.CacheDirective; 031 import net.dpml.transit.info.ProxyDirective; 032 import net.dpml.transit.info.TransitDirective; 033 import net.dpml.transit.model.CacheModel; 034 import net.dpml.transit.model.ProxyModel; 035 import net.dpml.transit.model.TransitModel; 036 import net.dpml.transit.model.DisposalEvent; 037 import net.dpml.transit.model.DisposalListener; 038 import net.dpml.transit.monitor.LoggingAdapter; 039 040 import net.dpml.util.EventQueue; 041 import net.dpml.util.Logger; 042 043 /** 044 * The DefaultTransitModel class maintains an active configuration of the 045 * Transit system. 046 * 047 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 048 * @version 1.0.1 049 */ 050 public class DefaultTransitModel extends DefaultModel implements TransitModel 051 { 052 // ------------------------------------------------------------------------ 053 // static 054 // ------------------------------------------------------------------------ 055 056 /** 057 * Default configuration url path. 058 */ 059 public static final String DEFAULT_PROFILE_PATH = "local:xml:dpml/transit/standard"; 060 061 /** 062 * Default configuration url path. 063 */ 064 public static final URI DEFAULT_PROFILE_URI = createStaticURI( DEFAULT_PROFILE_PATH ); 065 066 /** 067 * System property key used to hold an overriding configuration url. 068 */ 069 public static final String PROFILE_KEY = "dpml.transit.profile"; 070 071 private static final EventQueue EVENT_QUEUE = new EventQueue( "dpml.transit" ); 072 073 /** 074 * Return a model that is restricted to the secure local environment with 075 * no proxy setting or external hosts. 076 * @param logger the logging channel to assign to the model 077 * @return the transit model 078 */ 079 public static DefaultTransitModel getSecureModel( Logger logger ) 080 { 081 try 082 { 083 TransitDirective directive = new TransitDirective( null, new CacheDirective() ); 084 if( logger.isTraceEnabled() ) 085 { 086 ClassLoader system = ClassLoader.getSystemClassLoader(); 087 int id = System.identityHashCode( system ); 088 logger.trace( "system classloader id: " + id ); 089 } 090 return new DefaultTransitModel( EVENT_QUEUE, logger, directive ); 091 } 092 catch( Exception e ) 093 { 094 final String error = 095 "Unexpected error while constructing static secure model."; 096 throw new RuntimeException( error, e ); 097 } 098 } 099 100 /** 101 * Resolve the transit configuration using the default resource path 102 * <tt>local:xml:dpml/transit/config</tt>. If the resource does not exist a classic 103 * default scenario will be returned. 104 * 105 * @return the transit model 106 * @exception Exception if an error occurs during model construction 107 */ 108 public static DefaultTransitModel getDefaultModel() throws Exception 109 { 110 return getDefaultModel( "transit" ); 111 } 112 113 /** 114 * Resolve the transit configuration using the default resource path 115 * <tt>local:xml:dpml/transit/config</tt>. If the resource does not exist a classic 116 * default scenario will be returned. 117 * 118 * @param category the logging channel category name 119 * @return the transit model 120 * @exception Exception if an error occurs during model construction 121 */ 122 public static DefaultTransitModel getDefaultModel( String category ) throws Exception 123 { 124 LoggingAdapter adapter = new LoggingAdapter( category ); 125 return getDefaultModel( adapter ); 126 } 127 128 /** 129 * Resolve the transit configuration using the default resource path 130 * <tt>local:xml:dpml/transit/config</tt>. If the resource does not exist a classic 131 * default scenario will be returned. 132 * 133 * @param logger the logging channel 134 * @return the transit model 135 * @exception Exception if an error occurs during model construction 136 */ 137 public static DefaultTransitModel getDefaultModel( Logger logger ) throws Exception 138 { 139 String path = System.getProperty( PROFILE_KEY ); 140 if( logger.isTraceEnabled() ) 141 { 142 ClassLoader system = ClassLoader.getSystemClassLoader(); 143 int id = System.identityHashCode( system ); 144 logger.trace( "system classloader id: " + id ); 145 } 146 if( null != path ) 147 { 148 URL url = Artifact.createArtifact( path ).toURL(); 149 TransitBuilder builder = new TransitBuilder( logger ); 150 TransitDirective directive = builder.load( url ); 151 return new DefaultTransitModel( EVENT_QUEUE, logger, directive ); 152 } 153 else 154 { 155 File prefs = Transit.DPML_PREFS; 156 File config = new File( prefs, "dpml/transit/xmls/standard.xml" ); 157 if( config.exists() ) 158 { 159 URL url = config.toURL(); 160 TransitBuilder builder = new TransitBuilder( logger ); 161 TransitDirective directive = builder.load( url ); 162 return new DefaultTransitModel( EVENT_QUEUE, logger, directive ); 163 } 164 else 165 { 166 return getClassicModel( logger ); 167 } 168 } 169 } 170 171 // ------------------------------------------------------------------------ 172 // state 173 // ------------------------------------------------------------------------ 174 175 private final DefaultProxyModel m_proxy; 176 private final DefaultCacheModel m_cache; 177 178 // ------------------------------------------------------------------------ 179 // constructor 180 // ------------------------------------------------------------------------ 181 182 /** 183 * Creation of a new TransitModel using a supplied configuration 184 * and logging channel. The implementation will construct a proxy 185 * model, layout registry model, cache model, and repository codebase 186 * model using the supplied configuration. 187 * 188 * @param logger the assigned loging channel 189 * @param directive the transit configuration 190 * @exception NullPointerException if the logger or directive arguments are null 191 * @exception RemoteException if a remote exception occurs 192 */ 193 public DefaultTransitModel( Logger logger, TransitDirective directive ) 194 throws RemoteException, NullPointerException 195 { 196 this( EVENT_QUEUE, logger, directive ); 197 } 198 199 /** 200 * Creation of a new TransitModel using a supplied configuration 201 * and logging channel. The implementation will construct a proxy 202 * model, layout registry model, cache model, and repository codebase 203 * model using the supplied configuration. 204 * 205 * @param queue the event queue 206 * @param logger the assigned logging channel 207 * @param directive the transit configuration 208 * @exception NullPointerException if the logger or directive arguments are null 209 * @exception RemoteException if a remote exception occurs 210 */ 211 public DefaultTransitModel( EventQueue queue, Logger logger, TransitDirective directive ) 212 throws RemoteException, NullPointerException 213 { 214 super( queue, logger ); 215 216 if( null == directive ) 217 { 218 throw new NullPointerException( "directive" ); 219 } 220 221 m_proxy = createProxyModel( directive ); 222 m_cache = createCacheModel( directive ); 223 } 224 225 // ------------------------------------------------------------------------ 226 // TransitModel 227 // ------------------------------------------------------------------------ 228 229 /** 230 * Return the proxy configuration model. 231 * @return the proxy model (null if no proxy config defined). 232 */ 233 public ProxyModel getProxyModel() 234 { 235 return m_proxy; 236 } 237 238 /** 239 * Return the cache model. 240 * @return the cache model 241 */ 242 public CacheModel getCacheModel() 243 { 244 return m_cache; 245 } 246 247 /** 248 * Add a disposal listener to the model. 249 * @param listener the listener to add 250 */ 251 public void addDisposalListener( DisposalListener listener ) 252 { 253 super.addListener( listener ); 254 } 255 256 /** 257 * Remove a disposal listener from the model. 258 * @param listener the listener to remove 259 */ 260 public void removeDisposalListener( DisposalListener listener ) 261 { 262 super.removeListener( listener ); 263 } 264 265 /** 266 * Internal event handler. 267 * @param eventObject the event to handle 268 */ 269 public void processEvent( EventObject eventObject ) 270 { 271 if( eventObject instanceof DisposalEvent ) 272 { 273 DisposalEvent event = (DisposalEvent) eventObject; 274 processDisposalEvent( event ); 275 } 276 } 277 278 private void processDisposalEvent( DisposalEvent event ) 279 { 280 EventListener[] listeners = super.getEventListeners(); 281 for( int i=0; i < listeners.length; i++ ) 282 { 283 EventListener listener = listeners[i]; 284 if( listener instanceof DisposalListener ) 285 { 286 DisposalListener pl = (DisposalListener) listener; 287 try 288 { 289 pl.notifyDisposal( event ); 290 } 291 catch( Throwable e ) 292 { 293 final String error = 294 "Disposal notification error."; 295 getLogger().error( error, e ); 296 } 297 } 298 } 299 } 300 301 // ------------------------------------------------------------------------ 302 // impl 303 // ------------------------------------------------------------------------ 304 305 Logger getLoggingChannel() 306 { 307 return getLogger(); 308 } 309 310 /** 311 * Trigger disposal of the transit model. 312 */ 313 public synchronized void dispose() 314 { 315 DisposalEvent event = new DisposalEvent( this ); 316 enqueueEvent( event, true ); 317 disposeCacheModel(); 318 disposeProxyModel(); 319 super.dispose(); 320 EVENT_QUEUE.terminateDispatchThread(); 321 Thread thread = new Terminator( this ); 322 thread.start(); 323 } 324 325 /** 326 * Internal model terminator. 327 */ 328 private class Terminator extends Thread 329 { 330 private final DefaultTransitModel m_model; 331 Terminator( DefaultTransitModel model ) 332 { 333 m_model = model; 334 } 335 336 /** 337 * Initiate model retraction from the RMI. 338 */ 339 public void run() 340 { 341 try 342 { 343 UnicastRemoteObject.unexportObject( m_model, true ); 344 } 345 catch( NoSuchObjectException e ) 346 { 347 // ignore 348 } 349 catch( RemoteException e ) 350 { 351 e.printStackTrace(); 352 } 353 } 354 } 355 356 private synchronized void disposeProxyModel() 357 { 358 if( null == m_proxy ) 359 { 360 return; 361 } 362 else 363 { 364 m_proxy.dispose(); 365 try 366 { 367 UnicastRemoteObject.unexportObject( m_proxy, true ); 368 } 369 catch( NoSuchObjectException e ) 370 { 371 // ignore 372 } 373 catch( RemoteException e ) 374 { 375 getLogger().warn( "Remote error during proxy reference removal.", e ); 376 } 377 } 378 } 379 380 private synchronized void disposeCacheModel() 381 { 382 m_cache.dispose(); 383 try 384 { 385 UnicastRemoteObject.unexportObject( m_cache, true ); 386 } 387 catch( NoSuchObjectException e ) 388 { 389 // ignore 390 } 391 catch( RemoteException e ) 392 { 393 getLogger().warn( "Remote error during cache reference removal.", e ); 394 } 395 } 396 397 private DefaultProxyModel createProxyModel( final TransitDirective directive ) 398 { 399 try 400 { 401 ProxyDirective config = directive.getProxyDirective(); 402 if( null == config ) 403 { 404 return null; 405 } 406 else 407 { 408 Logger logger = getLogger().getChildLogger( "proxy" ); 409 return new DefaultProxyModel( EVENT_QUEUE, logger, config ); 410 } 411 } 412 catch( Throwable e ) 413 { 414 final String error = 415 "An error occured during construction of the proxy model."; 416 throw new TransitError( error, e ); 417 } 418 } 419 420 private DefaultCacheModel createCacheModel( final TransitDirective directive ) 421 { 422 try 423 { 424 Logger logger = getLogger().getChildLogger( "cache" ); 425 CacheDirective config = directive.getCacheDirective(); 426 return new DefaultCacheModel( EVENT_QUEUE, logger, config ); 427 } 428 catch( Throwable e ) 429 { 430 final String error = 431 "An error occured during construction of the cache model."; 432 throw new TransitError( error, e ); 433 } 434 } 435 436 static DefaultTransitModel getBootstrapModel() throws Exception 437 { 438 Logger logger = new LoggingAdapter( "transit" ); 439 return getSecureModel( logger ); 440 } 441 442 static DefaultTransitModel getClassicModel( Logger logger ) throws Exception 443 { 444 TransitDirective directive = TransitDirective.CLASSIC_PROFILE; 445 return new DefaultTransitModel( EVENT_QUEUE, logger, directive ); 446 } 447 448 private static URI createStaticURI( String path ) 449 { 450 try 451 { 452 return Artifact.createArtifact( path ).toURI(); 453 } 454 catch( Exception e ) 455 { 456 return null; 457 } 458 } 459 460 } 461